package com.common.frame.base


import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.common.frame.bean.*
import com.common.frame.extension.catch
import com.common.frame.net.NetLaunchManager
import com.common.frame.net.NetLaunchManager.response
import com.hjq.toast.ToastUtils
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import retrofit2.HttpException
import java.net.ConnectException
import java.net.UnknownHostException


open class BaseViewModel : ViewModel() {

    val showLoadingDialog by lazy { SingleLiveEvent<String>() }
    val dismissLoadingDialog by lazy { SingleLiveEvent<Void>() }
    val finishEvent by lazy { SingleLiveEvent<Void>() }
    val toastEvent by lazy { SingleLiveEvent<String>() }
    val dataEvent by lazy { SingleLiveEvent<Data>() }
    val customToastEvent by lazy { SingleLiveEvent<ToastBean>() }
    val tipsEvent by lazy { SingleLiveEvent<TipsEvent>() }




    private val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        Log.d("coroutine", throwable.message.toString())
    }

    fun launchUI(block: suspend CoroutineScope.() -> Unit) =
        viewModelScope.launch(exceptionHandler) {
            try {
                block()
            } catch (e: Exception) {
                if (e is HttpException || e is ConnectException || e is UnknownHostException || e is TimeoutCancellationException) {
                    ToastUtils.show("网络异常")
                } else {
                    ToastUtils.show(e.message ?: "")
                }
            }
        }

    fun <T> launchRequest(
        block: suspend () -> HttpResponse<T>,
        success: (T) -> Unit = {},
        error: (ResponseThrowable) -> Unit = { toastEvent.postValue(it.msg) },
        showLoading: Boolean = true,
        loadingTips: String = "加载中...",
        response: ((HttpResponse<T>) -> Unit)? = null,//返回全部数据
        withDelay: Long = 15000
    ) {
        if (showLoading) showLoadingDialog.postValue(loadingTips)
        launchUI {
            NetLaunchManager.launchRequestSuspend(block, success, error, response, {
                if (showLoading) dismissLoadingDialog.call()
            }, withDelay)
        }
    }

    suspend fun <T : Any> launchIO(call: suspend () -> T): T {
        return withContext(Dispatchers.IO) { call.invoke() }.apply {
        }
    }


    fun launchIO(block: suspend CoroutineScope.() -> Unit) =
        CoroutineScope(Dispatchers.IO).launch { block() }

    fun <T> Flow<T>.showLoad(showLoading: Boolean = true, loadingTips: String = "加载中..."): Flow<T> {
        return this.onStart {
            if (showLoading)
                showLoadingDialog.postValue(loadingTips)
        }.onCompletion {
            if (showLoading)
                dismissLoadingDialog.call()
        }
    }

    /**
     * 用流的方式进行网络请求
     */
    fun <T : Any> launchFlow(
        block: suspend () -> HttpResponse<T>,
        withDelay: Long = 15000
    ): Flow<HttpResponse<T>> {
        return flow {
            withTimeout(withDelay) {
                emit(block().response())
            }
        }.catch()
    }
}